<?php

Raise::load('core.RaiseDataType');
Raise::load('RaiseDateTime');
Raise::load('core.helper.sorting.IRaiseComparable');
Raise::load('RaiseDateComparator');

class RaiseDate extends RaiseDataType implements IRaiseComparable {

    /**
     * Day of the month (1 to 31)
     * @var integer
     */
    private $day = 0;

    /**
     * Month of the year (1 to 12)
     * @var integer
     */
    private $month = 0;

    /**
     * The year
     * @var integer
     */
    private $year = 0;
    
    /**
     * Create a new Grerogian calendar date with Year Month Day
     * @param integer $year
     * @param integer $month
     * @param integer $day 
     */
    public function __construct($year, $month, $day){
        $this->year($year);
        $this->month($month);
        $this->day($day);
    }
    
    private function processNextComponent($value, $next, $max){
        if($value >= $max){
            $addNext = (int)floor($value / $max);
            $this->$next($this->$next + $addNext);
            $value -= $addNext * $max;
        }elseif($value < 0){
            $subNext = (int)floor(abs($value) / $max);
            $this->$next($this->$next - $subNext - 1);
            $value = $max + ($value + ($subNext * $max));
        }
        return $value;
    }

    /**
     * The day component of the RaiseDateTime
     * @param integer $a An integer from 1 to 31 representing the days of a month
     * @return integer
     */
    public function day($a = null){
        if(func_num_args() == 1){
            if($a != $this->day){
                $a--;
                $a = $this->processNextComponent($a, 'month', RaiseDateTime::daysInMonth($this->month, $this->year)) + 1;
                $this->day = (int)$a;
            }
        }
        return $this->day;
    }

    /**
     * The month component of the RaiseDateTime
     * @param integer $a An integer from 1 to 12 representing the various months.
     * @return integer
     */
    public function month($a = null){
        if(func_num_args() == 1){
            if($a != $this->month){
                $a--;
                $a = $this->processNextComponent($a, 'year', 12) + 1;
                $this->month = (int)$a;
            }
        }
        return $this->month;
    }
    
    /**
     * The year component of the RaiseDateTime
     * @param integer $a An integer that represents the year
     * @return integer
     */
    public function year($a = null){
        if(func_num_args() == 1){
            if($a != $this->year){
                $this->year = (int)$a;
            }
        }
        return $this->year;
    }
    
    /**
     * Add another date to this date.
     * @param RaiseTimeSpan $timeSpan The amount of time to add
     * @return RaiseDate The resulting date from the addition operation
     */
    public function add($timeSpan){
        $temp = $this->raiseClone();
        
        $temp->day($temp->day + (int)floor($timeSpan->totalDays()));
        
        return $temp;
    }
    
    /**
     * Subtract some date from this date. 
     * @param RaiseTimeSpan $timeSpan The amount of time to deduct
     * @return RaiseDate The resulting date from the subtract operation
     */
    public function subtract($timeSpan){
        $temp = $this->raiseClone();
        
        $temp->day($temp->day - (int)floor($timeSpan->totalDays()));
        
        return $temp;
    }
    
    /**
     * Get the total number of days in this date
     * @return integer
     */
    public function totalDays(){
        $deltaYear = $this->year - 1;
        $leapCount = round(($deltaYear / 4) - ($deltaYear / 100) + ($deltaYear / 400));
        return $this->day + RaiseDateTime::daysInMonth($this->month, $this->year) + $leapCount * 366 + ($this->year - $leapCount) * 365;
    }

    public function compareTo($date) {
        $comparator = new RaiseDateComparator();
        return $comparator->compare($this, $date);
    }
    
}